//4.1.1
/**
 * @param {string} str1
 * @remark JavaScript 是一种动态类型语言，变量没有类型限制，可以随时赋予任意值。
 */
function first1 (str1){
    var y=true;
    var x = y ? 1 : 'a';
    return x
}
// console.log(first1())//1

//4.1.2-1
/**
 * @param {string} str2
 * @remark 使用Number函数，可以将任意类型的值转化成数值。
 */
function first2 (str2){
    return{
        return1:Number(324) ,   //数值：转换后还是原来的值
        return2:Number('324'), //字符串：如果可以被解析为数值，则转换为相应的数值
        return3:Number('abc'), //字符串：如果不能被解析为数值，则转换为NaN
        return4:Number(true), //布尔值：true为1，false为0
        return5:Number(null), //null：转换为0
        return6:Number(undefined), //undefined：转换为NaN
        return7:Number(NaN), //NaN：本身
        return8:Number(Infinity), //正无穷：本身
        return9:Number('') //空字符串：转换为0
    }
}

/**
 * @param  {string} str3
 * @remark 只要有一个字符无法转成数值，整个字符串就会被转为NaN。
 */
function first3 (str3){
    return{
        return1:parseInt('42 cats'),
        return2:Number('42 cats')
    }
}

/**
 * @param  {string} str4
 * @remark parseInt和Number函数都会自动过滤一个字符串前导和后缀的空格。
 * @remark parseInt() 函数解析字符串并返回整数。
 */
function first4 (str4){
    return{
        return1:parseInt('\t\v\r12.34\n') ,
        return2:Number('\t\v\r12.34\n')
    }
}

// console.log(first2())
/*
{
  return1: 324,
  return2: 324,
  return3: NaN,
  return4: 1,
  return5: 0,
  return6: NaN,
  return7: NaN,
  return8: Infinity,
  return9: 0
}
*/
// console.log(first3())//{ return1: 42, return2: NaN }
// console.log(first4())//{ return1: 12, return2: 12.34 }

//4.1.2-2
/**
 * @param {string} str5
 * @remark Number方法的参数是对象时，将返回NaN，除非是包含单个数值的数组。
 */
function first5 (str5){
    return{
        return1:Number({a: 1}) ,
        return2:Number([1, 2,3]) ,
        return3:Number([1]) 
    }
}

/**
 * @param {string} str6
 * @remark Number背后的转换规则
 * @remark toSring和valueof 两者都是对象的方法！前者返回对象的字符串，后者返回对象的原始值，如果没有原始值则返回对象本身！
 */
function first6 (str6){
    var obj = {x: 1};
    return Number(obj)
    // if (typeof obj.valueOf() === 'object') {
    //     Number(obj.toString());
    //   } else {
    //     Number(obj.valueOf());
    //   }

    //typeof obj.valueOf()返回{ x: 1 }，是一个对象，所以调用toString方法返回字符串'[object Object]'
    //默认的 toString 方法返回一个字符串，表示对象的类型和内部标识。对于普通的对象，它会返回类似 "[object Object]" 的字符串
}

/**
 * @param {string} str7
 * @remark 对象的valueOf方法返回对象本身，所以一般总是会调用toString方法，而toString方法返回对象的类型字符串（比如[object Object]）
 * @remark obj.valueOf()返回NaN（一个特殊的值），则调用number函数，number（NaN）= NaN
 */
function first7 (str7){
    return Number({}) 
}

/**
 * @param {string} str8
 * @remark 如果toString方法返回的不是原始类型的值，结果就会报错。
 * @remark {}是对象。
 */
function first8 (str8){
    var obj = {
        valueOf: function () {
          return {};
        },
        toString: function () {
          return {};
        }
      };
      
    return Number(obj)  
}

/**
 * @param {string} str9
 * @remark valueOf和toString方法，都是可以自定义的。
 * @remark 在number里valueOf方法先于toString方法执行。
 */
function first9 (str9){
    var obj = {
        valueOf: function () {
          return 1;
        },
        toString: function () {
          return '2';
        }
      };
      
    return Number(obj)  
}

// console.log(first5())//{ return1: NaN, return2: 1, return3: 1 }
// console.log(first6())//NaN
// console.log(first7())//NaN
// console.log(first8())//Cannot convert object to primitive value
// console.log(first9())//1

//4.1.2-2
/**
 * @param {string} str10
 * @remark String函数可以将任意类型的值转化成字符串
 * @remark 原始类数值
 */
function first10 (str10){
    return {
        return1:String(123),
        return2:String(true),
        return3:String(null),
        return4:String(undefined),
        return5:String('abc'),
        return6:String(NaN)
    }
}

/**
 * @param {string} str10
 * @remark String方法的参数如果是对象，返回一个类型字符串；如果是数组，返回该数组的字符串形式。
 */
function first10 (str10){
    return {
        return1:String({a: 1}),
        return2:String([1, 2,3])
    }
}

/**
 * @param {string} str10
 * @remark 通过自定义toString方法，改变返回值的例子。
 * @remark 最终的运行结果是字符串 "[object Object]"，而不是数字 2。这是因为当 JavaScript 尝试将一个对象转换为字符串时，它首先查找对象的 toString 方法，然后才考虑 valueOf 方法。在这个例子中，由于缺少自定义的 toString 方法，因此默认的 Object.prototype.toString 方法被调用。
 * @remark 默认的 Object.prototype.toString 方法返回一个字符串，其中包含对象的类型信息和标识符。对于普通的对象，它的返回结果通常是 "[object Object]"。
 */
function second10 (str10){
    var value={
        valueOf: function () {
          return 2;
        }
    }
    return String(value)
}
// console.log(first10())//{ return1: '123', return2: 'true', return3: 'null', return4: 'undefined', return5: 'abc', return6: 'NaN' }
// console.log(first10())//{ return1: '[object Object]', return2: '1,2,3' }
// console.log(second10())//[object Object]


//4.1.2-3
/**
 * @param {string} str11
 * @remark Boolean()函数可以将任意类型的值转为布尔值。
 * @remark 所有对象（包括空对象）的转换结果都是true,false对应的布尔对象new Boolean(false)也是true
 */
function first11 (str11){
    return {
        return1:Boolean(0),
        return2:Boolean(''),
        return3:Boolean({}),
        return4:Boolean([]),
        return5:Boolean(NaN),
        return6:Boolean(Infinity),
        return7:Boolean(-Infinity),
        return8:Boolean(undefined),
        return9:Boolean(null),
        return10:Boolean(new Boolean(false))
    }
}

/**
 * @param {string} str12
 * @remark true和false这两个布尔值不会发生变化。
 */
function first12 (str12){
    return {
        return1:Boolean(true),
        return2:Boolean(false)
    }
}

// console.log(first11())//{ return1: false, return2: false, return3: true, return4: true, return5: false, return6: true, return7: false, return8: false, return9: false , return10: true }
// console.log(first12())//{ return1: true, return2: false }

//4.1.3
/**
 * @param {string} str13
 * @remark 不同类型的数据互相运算。JavaScript 会自动转换数据类型.
 */
function first13 (str13){
    return 123 + 'abc'
}

/**
 * @param {string} str14
 * @remark 对非布尔值类型的数据求布尔值。
 * @remark Boolean() 函数用于将一个值转换为布尔类型
 */
function first14 (str14){
    return Boolean(123)
}

/**
 * @param {string} str15
 * @remark 对非数值类型的值使用一元运算符（即+和-）
 */
function first15 (str15){
    return {
        return1:+ {foo: 'bar'},
        return2:- [1, 2, 3] 
    }
}

// console.log(first13())//123abc
// console.log(first14())//true
// console.log(first15())//{ return1: NaN, return2: NaN }

//4.1.3-1
/**
 * @param {string} str16
 * @remark JavaScript 遇到预期为布尔值的地方（比如if语句的条件部分），就会将非布尔值的参数自动转换为布尔值。
 * @remark 条件部分的每个值都相当于false，使用否定运算符后，就变成了true。
 * @remark 逻辑非（!）运算符具有更高的优先级（precedence）比逻辑与（&&）运算符。
 */
function first16 (str16){
    if ( !undefined
        && !null
        && !0
        && !NaN
        && !''
      ) { 
        console.log('true');
    }
}

/**
 * @param {string} str17
 * @remark 有时也用于将一个表达式转为布尔值。它们内部调用的也是Boolean()函数。
 */
function first17 (str17){
    var expression = 123;
    return {
        return1:expression ? true : false,
        return2:!!expression
    }
}

// console.log(first16())//true undeifned 
// console.log(first17())//{ return1: false, return2: false }

//4.1.3-2
/**
 * @param {string} str18
 * @remark 字符串的自动转换，主要发生在字符串的加法运算时。当一个值为字符串，另一个值为非字符串，则后者转为字符串。
 */
function first18 (str18){
    return {
        return1: 'abc' + 123,
        return2: 'abc' - 123,
        return3: 'abc' * 123,
        return4: 'abc' / 123,
        return5: 'abc' + true,
        return6: 'abc' + false,
        return7: 'abc' + null,
        return8: 'abc' + undefined,
        return9: 'abc' + {},
        return10: 'abc' + [],
        return11: 'abc' + function(){}
    }
}

/**
 * @param {string} str19
 * @remark 这种自动转换很容易出错。
 * @remark 期望返回120，但是由于自动转换，实际上返回了一个字符10020。
 */
function first19 (str19){
    var obj = {
        width: '100'
      };
      
    return obj.width + 20 
}

// console.log(first18())//{ return1: 'abc123', return2: NaN, return3: NaN, return4: NaN, return5: 'abctrue', return6: 'abctrue', return7: 'abcnull', return8: 'abcundefined, return9: 'abc[object Object]', return10: 'abc', return11: 'abcfunction(){}' }
// console.log(first19())//10020

//4.1.3-3
/**
 * @param {string} str20
 * @remark 除了加法运算符（+）有可能把运算子转为字符串，其他运算符都会把运算子自动转成数值。
 */
function first20 (str20){
    return {
        return1: 123 * 'abc',
        return2: 123 / '2',
        return3: 123 % 'abc'
    }
}

/**
 * @param {string} str21
 * @remark 一元运算符也会把运算子转成数值。
 */
function first21 (str21){
    return {
        return1: +'abc',
        return2: -'abc',
        return3: +true,
        return4: -false
    }
}

// console.log(first20())//{ return1: NaN, return2: 61.5, return3: NaN }
// console.log(first21())//{ return1: NaN, return2: NaN, return3: 1, return4:-0}

//4.2.1
/**
 * @param {string} str22
 * @remark JavaScript 原生提供Error构造函数，所有抛出的错误都是这个构造函数的实例。
 */
function first22 (str22){
    var err = new Error('出错了');
    return err.message 
}

/**
 * @param {string} str23
 * @remark 使用name和message这两个属性，可以对发生什么错误有一个大概的了解。
 */
function first23 (str23){
    var err = new Error('出错了');
    return {
        name:err.name,
        message:err.message
    }
}

/**
 * @param {string} str24
 * @remark stack属性用来查看错误发生时的堆栈。
 */
function first24 (str24){
    function throwit() {
        throw new Error('');
      }
      
      function catchit() {
        try {
          throwit();
        } catch(e) {
          console.log(e.stack); // print stack trace
        }
      }
      
    return catchit()
}

// console.log(first22())//出错了
// console.log(first23())//{ name: 'Error', message: '出错了' }
// console.log(first24())//Error

//4.2.2-1
/**
 * @param {string} str25
 * @remark SyntaxError对象是解析代码时发生的语法错误。
 */
// function first25 (str25){
//     // 变量名错误
//     var 1a;
//     return 1a
// }

/**
 * @param {string} str26
 * @remark 同上
 */
function first26 (str26){
    // 缺少括号
    // console.log 'hello');
}
// console.log(first25())//SyntaxError: Invalid or unexpected token
// console.log(first26())//SyntaxError: Unexpected string

//4.2.2-2
/**
 * @param {string} str27
 * @remark ReferenceError对象是引用一个不存在的变量时发生的错误。
 */
function first27 (str27){
    // 使用一个不存在的变量
    unknownVariable
}

/**
 * @param {string} str28
 * @remark 另一种触发场景是，将一个值分配给无法分配的对象，比如对函数的运行结果赋值。
 */
function first28 (str28){
    // 等号左侧不是变量
    console.log() = 1
}

// console.log(first27())//ReferenceError: unknownVariable is not defined
// console.log(first28())//ReferenceError: Invalid left-hand side in assignment

//4.2.2-3
/**
 * @param {string} str29
 * @remark RangeError对象是一个值超出有效范围时发生的错误。
 */
function first29 (str29){
    // 数组长度不得为负数
    // new Array(-1)
}

//console.log(first29())//RangeError: Invalid array length

//4.2.2-4
/**
 * @param {string} str30
 * @remark TypeError对象是变量或参数不是预期类型时发生的错误。
 * @remark 因为new命令的参数应该是一个构造函数。
 */
function first30 (str30){
    // new 123
}

/**
 * @param {string} str31
 * @remark 调用对象不存在的方法，也会抛出TypeError错误，因为obj.unknownMethod的值是undefined，而不是一个函数。
 */
function first31 (str31){
    // var obj = {};
    // obj.unknownMethod()
}

// console.log(first30())//TypeError: 123 is not a constructor
// console.log(first31())//TypeError: obj.unknownMethod is not a function

//4.2.2-5
/**
 * @param {string} str32
 * @remark URIError对象是URI相关函数的参数不正确时抛出的错误。
 */
function first32 (str32){
    // decodeURI('%2')
}
// console.log(first32())//URIError: URI malformed

//4.2.2-6
/**
 * @param {string} str33
 * @remark eval函数没有被正确执行时，会抛出EvalError错误
 * @remark 该错误类型已经不再使用了，只是为了保证与以前代码兼容，才继续保留。
 */
function first33 (str33){
    // eval('var f = 123')
}
// console.log(first33())//undefined

//4.2.2-7
/**
 * @param {string} str34
 * @remark 以上这6种派生错误，连同原始的Error对象，都是构造函数
 */
function first34 (str34){
    var err1 = new Error('出错了！');
    var err2 = new RangeError('出错了，变量超出有效范围！');
    var err3 = new TypeError('出错了，变量类型无效！');
    return{
        return1: err1.message,
        return2: err2.message,
        return3: err3.message
    }
}

// console.log(first34())//{ return1: '出错了！', return2: '出错了，变量超出有效范围！', return3: '出错了，变量类型无效！' }

//4.2.3
/**
 * @param {string} str35
 * @remark 除了 JavaScript 原生提供的七种错误对象，还可以定义自己的错误对象
 */
function first35 (str35){
    // function UserError(message) {
    //     this.message = message || '默认信息';
    //     this.name = 'UserError';
    // }
    // return {
    //     return1:UserError.prototype = new Error(),
    //     return2:UserError.prototype.constructor = UserError
    // }
}

// console.log(first35())//{ return1: [object Error], return2: function UserError() { [native code] } }

//4.2.4
/**
 * @param {string} str36
 * @remark throw语句的作用是手动中断程序执行，抛出一个错误。
 */
function first36 (str36){
    // var x = -1;

    // if (x <= 0) {
    //     throw new Error('x 必须为正数');
    // }
}

/**
 * @param {string} str37
 * @remark throw也可以抛出自定义错误。
 */
function first37 (str37){
    // function UserError(message) {
    //     this.message = message || '默认信息';
    //     this.name = 'UserError';
    //   }
    // throw new UserError('出错了！');
}

/**
 * @param {string} str38
 * @remark throw可以抛出任何类型的值。也就是说，它的参数可以是任何值。
 */
function first38 (str38){
    // throw '出错了！';
}

// console.log(first36())//Error: x 必须为正数
// console.log(first37())//UserError { message: '出错了！', name: 'UserError' }
// console.log(first38())//出错了！

//4.2.5
/**
 * @param {string} str39
 * @remark JavaScript 提供了try...catch结构，允许对错误进行处理，选择是否往下执行。
 * @remark catch代码块捕获错误之后，程序不会中断，会按照正常流程继续执行下去。
 */
function first39 (str39){
    try {
        throw new Error('出错了!');
      } catch (e) {
        console.log(1);
        console.log(2);
    }
}

/**
 * @param {string} str40
 * @remark catch代码块之中，还可以再抛出错误，甚至使用嵌套的try...catch结构。
 */
function first40 (str40){
    var n = 100;

    try {
      throw n;
    } catch (e) {
      if (e <= 50) {
        // ...
      } else {
        throw e;
      }
    }
}

/**
 * @param {string} str41
 * @remark 为了捕捉不同类型的错误，catch代码块之中可以加入判断语句。
 */
function first41 (str41){
    try {
      throw new Error('出错了!');
    } catch (e) {
      if (e instanceof TypeError) {
        console.log(1);
      } else if (e instanceof RangeError) {
        console.log(2);
      } else {
        console.log(3);
      }
    }
}

// console.log(first39())
/*
1
2
undefined
*/
// console.log(first40())// Uncaught 100
// console.log(first41())// 3 undefined

//4.2.6
/**
 * @param {string} str42
 * @remark try...catch结构允许在最后添加一个finally代码块，表示不管是否出现错误，都必需在最后运行的语句。
 */
function first42 (str42){
    function cleansUp() {
        try {
          throw new Error('出错了……');
          console.log('此行不会执行');
        } finally {
          console.log('完成清理工作');
        }
    }  
    return cleansUp()
}

/**
 * @param {string} str43
 * @remark try代码块没有发生错误，而且里面还包括return语句，但是finally代码块依然会执行。而且，这个函数的返回值还是result。
 */
function first43 (str43){
    function idle(x) {
        try {
          console.log(x);
          return 'result';
        } finally {
          console.log('FINALLY');
        }
    }
    return idle('hello');
}

/**
 * @param {string} str43
 * @remark 如果你调用 countUp()，它的返回值会是 0，因为 return count; 在 count 被递增之前执行。如果你再次调用 countUp()，那么它的返回值会是 1，因为在上一次调用中 count 已经被递增了。
 */
function second43 (str43){
    var count = 0;
   function countUp() {
    try {
        return count;
    } finally {
        count++;
    }
}
    return{
        return1:countUp(),
        return2:count
    }
}


/**
 * @param {string} str44
 * @remark try...catch...finally这三者之间的执行顺序。
 * @remark 如果没有发生错误，则运行finally代码块关闭文件；一旦发生错误，则先使用catch代码块处理错误，再使用finally代码块关闭文件。
 * @remark return语句的执行是排在finally代码之前，只是等finally代码执行完毕后才返回。
 */
function first44 (str44){
    function f() {
        try {
          console.log(0);
          throw 'bug';
        } catch(e) {
          console.log(1);
          return true; // 这句原本会延迟到 finally 代码块结束再执行
          console.log(2); // 不会运行
        } finally {
          console.log(3);
          return false; // 这句会覆盖掉前面那句 return
          console.log(4); // 不会运行
        }
      
        console.log(5); // 不会运行
    }
    var result = f();
    console.log(6)
    return result
}
// console.log(first42())//完成清理工作 Error: 出错了……
// console.log(first43())// hello FINALLY result
// console.log(second43())// { return1: 0, return2: 1 }
// console.log(first44())// 0 1 3 6 false

//4.3.3
/**
 * @param {string} str45
 * @remark 建议总是使用大括号表示区块。
 * @remark JavaScript 要使用后一种，因为 JavaScript 会自动添加句末的分号，导致一些难以察觉的错误。
 */
function first45 (str45){
    return
    {
        key: value
    };

    // 相当于
    // return;
    // {
    //     key: value
    // };
    //正确
    /*
    return {
        key : value
    };
    */
}

//4.3.4
/**
 * @param {string} str46
 * @remark 圆括号（parentheses）在 JavaScript 中有两种作用，一种表示函数的调用，另一种表示表达式的组合（grouping）。
 * @remark 表示函数调用时，函数名与左括号之间没有空格。表示函数定义时，函数名与左括号之间没有空格。其他情况时，前面位置的语法元素与左括号之间，都有一个空格
 */
function first46 (str46){
    // 圆括号表示函数的调用
    console.log('abc');

    // 圆括号表示表达式的组合
    (1 + 2) * 3
}

//4.3.5-1
/**
 * @remark for 和 while 循环,if，switch，try,函数的声明语句不需要在结尾添加分号.
 * @remark do...while循环是有分号的。函数表达式仍然要使用分号。
 */

//4.3.5-2
/**
 * @param {string} str47
 * @remark 如果没有使用分号，大多数情况下，JavaScript 会自动添加。
 */
function first47 (str47){
    var a = 1
    // 等同于
    var a = 1;
}

/**
 * @param {string} str48
 * @remark 如果下一行的开始可以与本行的结尾连在一起解释，JavaScript 就不会自动添加分号。
 */
function first48 (str48){
    x = y
    (function () {
      // ...
    })();
    
    // 等同于
    // x = y(function () {...})();

    if (a < 0) a = 0
    console.log(a)

    // 等同于下面的代码，
    // 因为 0console 没有意义
    if (a < 0) a = 0;
    console.log(a)
}

/**
 * @param {string} str49
 * @remark 如果一行的起首是“自增”（++）或“自减”（--）运算符，则它们的前面会自动添加分号。
 */
function first49 (str49){
    a = b = c = 1

    a
    ++
    b
    --
    c

    // 等同于
    // a = b = c = 1;
    // a;
    // ++b;
    // --c;
    
    console.log(a, b, c)
}

/**
 * @param {string} str50
 * @remark 如果continue、break、return和throw这四个语句后面，直接跟换行符，则会自动添加分号
 */
function first50 (str50){
    return
    { first: 'Jane' };

    // 解释成
    return;
    { first: 'Jane' };
}

/**
 * @param {string} str51
 * @remark 不写结尾的分号，可能会导致脚本合并出错。所以，有的代码库在第一行语句开始前，会加上一个分号。
 */
function first51 (str51){
    ;[1, 2, 3].forEach(alert);
}

// console.log(first49())// 1,2,3 undefined

//4.3.6
/**建议避免使用全局变量。如果不得不使用，可以考虑用大写字母表示变量名，这样更容易看出这是全局变量，比如UPPER_CASE。 */

//4.3.7
/**
 * @param {string} str52
 * @remark JavaScript 会自动将变量声明“提升”（hoist）到代码块（block）的头部。
 * @remark 为了避免可能出现的问题，最好把变量声明都放在代码块的头部---存在一个全局的循环变量i.
 */
function first52 (str52){
    if (!x) {
        var x = {};
      }
      
      // 等同于
      var x;
      if (!x) {
        x = {};
      }
}

//4.3.8
/**
 * @remark with可以减少代码的书写，但是会造成混淆。因此，不要使用with语句。
 */

//4.3.9
/**
 * @param {string} str53
 * @remark JavaScript 有两个表示相等的运算符：“相等”（==）和“严格相等”（===）
 * @remark 相等运算符会自动转换变量类型，造成很多意想不到的情况。
 * @remark 建议不要使用相等运算符（==），只使用严格相等运算符（===）
 */
function first53 (str53){
    0 == ''// true
    1 == true // true
    2 == true // false
    0 == '0' // true
    false == 'false' // false
    false == '0' // true
    ' \t\r\n ' == 0 // true
}

//4.3.10
/**
 * @param {string} str54
 * @remark 不要将不同目的的语句，合并成一行。
 */
function first54 (str54){
    if (a = b) {
        // ...
    }
    //误解
    if (a === b){
        // ...
    }
}

//4.3.11
/**
 * @param {string} str55
 * @remark 自增（++）和自减（--）运算符，放在变量的前面或后面，返回的值不一样，很容易发生错误。
 * @remark 建议自增（++）和自减（--）运算符尽量使用+=和-=代替。
 */
function first55 (str55){
    ++x
    // 等同于
    x += 1;
}

//4.3.12
/**
 * @param {string} str56
 * @remark 建议switch...case结构可以用对象结构代替。
 */
function first56 (str56){
    //对象结构
    function doAction(action) {
        var actions = {
          'hack': function () {
            return 'hack';
          },
          'slash': function () {
            return 'slash';
          },
          'run': function () {
            return 'run';
          }
        };
      
        if (typeof actions[action] !== 'function') {
          throw new Error('Invalid action.');
        }
      
        return actions[action]();
    }
    
    //switch...case结构
    /*
    function doAction(action) {
        switch (action) {
            case 'hack':
             return 'hack';
            case 'slash':
                return 'slash';
            case 'run':
                return 'run';
            default:
                throw new Error('Invalid action.');
        }
    }
    */
}

//4.4.2-1
/**
 * @param {string} str57
 * @remark console.log方法用于在控制台输出信息。它可以接受一个或多个参数，将它们连接起来输出。
 */
function first57 (str57){
    console.log('Hello World')
    console.log('a', 'b', 'c')
}

/**
 * @param {string} str58
 * @remark 如果第一个参数是格式字符串（使用了格式占位符），console.log方法将依次用后面的参数替换占位符，然后再进行输出。
 */
function first58 (str58){
    console.log('Hello %s', 'World')
    console.log(' %s + %s = %s', 1, 1, 2)
}

/**
 * @param {string} str59
 * @remark 不同类型的数据必须使用对应的占位符。
 */
function first59 (str59){
    var number = 11 * 9;
    var color = 'red';
    
    console.log('%d %s balloons', number, color);
}

/**
 * @param {string} str60
 * @remark 使用%c占位符时，对应的参数必须是 CSS 代码，用来对输出内容进行 CSS 渲染。
 */
function first60 (str60){
    console.log(
        '%cThis text is styled!',
        'color: red; background: yellow; font-size: 24px;'
    )
}

/**
 * @param {string} str61
 * @remark 如果参数是一个对象，console.log会显示该对象的值。
 */
function first61 (str61){
    var obj = {
        name: '张三',
        age: 18,
        gender: true
    }
    console.log(obj)
    console.log({foo: 'bar'})
    console.log(Date)
}

/**
 * @param {string} str62
 * @remark console对象的所有方法，都可以被覆盖。
 * @remark console.log('Hello World')调用了刚才创建的 console 对象的 log 方法，并传入了字符串 'Hello World'。因为 log 方法现在什么也不做，所以这行代码不会在控制台打印任何东西。
 */
function first62 (str62){
    var console = {
        log: function () {}
    }
    console.log('Hello World')
}

// console.log(first57())
/*
 Hello World
 a b c
 undefined
 */
// console.log(first58())
/*
 Hello World
 1 + 1 = 2
 undefined
 */
// console.log(first59())
/*
 99 red balloons
 undefined
 */
// console.log(first60())
/*
 This text is styled!
 undefined
 浏览器运行，输出的内容将显示为黄底红字。
 */
// console.log(first61())
/*
 { name: '张三', age: 18, gender: true }
 { foo: 'bar' }
 [Function: Date]
 undefined*/
// console.log(first62())//undefined

//4.4.2-2
/**
 * @param {string} str63
 * @remark warn方法输出信息时，在最前面加一个黄色三角，表示警告；
 * @remark error方法输出信息时，在最前面加一个红色的叉，表示出错。同时，还会高亮显示输出文字和错误发生的堆栈。其他方面都一样。
 */
function first63 (str63){
    console.error('Error: %s (%i)', 'Server is not responding', 500)
    console.warn('Warning! Too few nodes (%d)',1)
}
// console.log(first63())
/*
 Error: Server is not responding (500)
 Warning! Too few nodes (1)
 undefined */

//4.4.2-3
/**
 * @param {string} str64
 * @remark 对于某些复合类型的数据，console.table方法可以将其转为表格显示。
 * @remark file extension翻译：（计算机）文件扩展名
 */
 function first64 (str64){
    var languages = [
        { name: "JavaScript", fileExtension: ".js" },
        { name: "TypeScript", fileExtension: ".ts" },
        { name: "CoffeeScript", fileExtension: ".coffee" }
    ];
      
    console.table(languages);
}

/**
 * @param {string} str65
 * @remark 下面是显示表格内容的例子。
 */
function first65 (str65){
    var languages = {
        csharp: { name: "C#", paradigm: "object-oriented" },
        fsharp: { name: "F#", paradigm: "functional" }
    };
      
    console.table(languages);      
}

// console.log(first64())
/*
┌─────────┬────────────────┬───────────────┐
│ (index) │      name      │ fileExtension │
├─────────┼────────────────┼───────────────┤
│    0    │  'JavaScript'  │     '.js'     │
│    1    │  'TypeScript'  │     '.ts'     │
│    2    │ 'CoffeeScript' │   '.coffee'   │
└─────────┴────────────────┴───────────────┘
undefined
*/
// console.log(first65())
/*
┌─────────┬──────┬───────────────────┐
│ (index) │ name │     paradigm      │
├─────────┼──────┼───────────────────┤
│ csharp  │ 'C#' │ 'object-oriented' │
│ fsharp  │ 'F#' │   'functional'    │
└─────────┴──────┴───────────────────┘
undefined
*/

//4.4.2-4
/**
 * @param {string} str66
 * @remark count方法用于计数，输出它被调用了多少次。
 */
function first66 (str66){
    function greet(user) {
        console.count();
        return 'hi ' + user;
      }
      
    return {
        return1:greet('bob'),//第一次调用了greet方法，count方法输出1
        return2:greet('alice'),//第二次调用了greet方法，count方法输出2
        return3:greet('bob')//第三次调用了greet方法，count方法输出3
    }
}

/**
 * @param {string} str67
 * @remark 该方法可以接受一个字符串作为参数，作为标签，对执行次数进行分类。
 */
function  first67 (str67){
    function greet(user) {
        console.count(user);
        return 'hi ' + user;
      }
      
    return {
        return1:greet('bob'),
        return2:greet('alice'),
        return3:greet('bob')
    }
}

// console.log(first66())
/*
 default: 1
 default: 2
 default: 3
 { return1: 'hi bob', return2: 'hi alice', return3: 'hi bob' } 
*/
// console.log(first67())
/*
 bob: 1
 alice: 1
 bob: 2
 { return1: 'hi bob', return2: 'hi alice', return3: 'hi bob' }
*/

//4.4.2-5
/**
 * @param {string} str68
 * @remark dir方法用来对一个对象进行检查（inspect），并以易于阅读和打印的格式显示。
 * @remark 该方法对于输出 DOM 对象非常有用，因为会显示 DOM 对象的所有属性。
 * @remark dirxml方法主要用于以目录树的形式，显示 DOM 节点。
 * @remark 在JavaScript中，__proto__是一个特殊的属性，它用于定义一个对象的原型。
 */
function first68 (str68){
    console.dir({f1: 'foo', f2: 'bar'})
}

/**
 * @param {string} str69
 * @remark Node 环境之中，还可以指定以代码高亮的形式输出。
 */
function first69 (str69){
    console.dir({f1: 'foo', f2: 'bar'}, {colors: true})
}

/**
 * @param {string} str70
 * @remark 如果参数不是 DOM 节点，而是普通的 JavaScript 对象，console.dirxml等同于console.dir。
 */
function first70 (str70){
    console.dirxml([1, 2, 3])
    console.dir([1, 2, 3])
}

// console.log(first68())
/*
 //node
 { f1: 'foo', f2: 'bar' }
 undefined
 //浏览器
 Object
 f1: "foo"
 f2: "bar"
 __proto__: Object
 */
// console.log(first69())
/*
 //true
 'foo', 'bar'亮高
 //false
 无亮高
*/
// console.log(first70())
/*
[ 1, 2, 3 ]
[ 1, 2, 3 ]
undefined
*/

//4.4.2-6
/**
 * @param {string} str71
 * @remark console.assert方法主要用于程序运行过程中，进行条件判断，如果不满足条件，就显示一个错误，但不会中断程序执行。
 */
function first71 (str71){
    console.assert(false, '判断条件不成立')
}
// console.log(first71())
// Assertion failed: 判断条件不成立
// undefined

//4.4.2-7
/**
 * @param {string} str72
 * @remark console.time()，console.timeEnd() 这两个方法用于计时，可以算出一个操作所花费的准确时间
 * @remark 这个函数只是创建了一个包含100万个空对象的数组，并记录了创建这个数组所需的时间。
 */
function first72 (str72){
    console.time('Array initialize');

    var array= new Array(1000000);
    for (var i = array.length - 1; i >= 0; i--) {
        array[i] = new Object();
    };

    console.timeEnd('Array initialize');
}
// console.log(first72())
//Array initialize: 103.317ms
// undefined

//4.4.2-8
/**
 * @param {string} str73
 * @remark console.group和console.groupEnd这两个方法用于将显示的信息分组。它只在输出大量信息时有用，分在一组的信息，可以用鼠标折叠/展开。
 */
function first73 (str73){
    console.group('一级分组');
    console.log('一级分组的内容');
    console.groupEnd();

    console.group('二级分组');
    console.log('二级分组的内容');
    console.groupEnd();
}
function second73 (str73){
    console.group('一级分组');
    console.log('一级分组的内容');
    
    console.group('二级分组');
    console.log('二级分组的内容');
    
    console.groupEnd(); 
    console.groupEnd(); 
}

/**
 * @param {string} str73
 * @remark console.groupCollapsed方法与console.group方法很类似，唯一的区别是该组的内容，在第一次显示时是收起的（collapsed），而不是展开的
 */
function third73 (str73){
    console.groupCollapsed('Fetching Data');

    console.log('Request Sent');
    console.error('Error: Server not responding (500)');
    
    console.groupEnd();
}

// console.log(first73())
/*
 一级分组
   一级分组的内容
 二级分组
   二级分组的内容
 undefined
*/
// console.log(second73())
/*
 一级分组
   一级分组的内容
   二级分组
     二级分组的内容
 undefined
*/
// console.log(third73())
/*
 //node 
 Fetching Data
  Request Sent
  Error: Server not responding (500)
 undefined
*/

//4.4.2-9
/**
 * @param {string} str74
 * @remark console.trace方法显示当前执行的代码在堆栈中的调用路径。
 */
function first74 (str74){
    console.trace()
}
// console.log(first74())

//4.4.3
/**
 * @param {string} str75
 * @remark $_属性返回上一个表达式的值
 * @remark 控制台保存了最近5个在 Elements 面板选中的 DOM 元素，$0代表倒数第一个（最近一个），$1代表倒数第二个，以此类推直到$4。
 */
function first75 (str75){
    return 2 + 2
}
function second75 (str75){
    return $_
}

/**
 * @param {string} str76
 * @remark $x(path)方法返回一个数组，包含匹配特定 XPath 表达式的所有 DOM 元素。
 */
function first76 (str76){
     $x('//div[@id="foo"]')
}

// console.log(first75())//4
/*
在node环境不能用
// console.log(second75())//4
// console.log(first76())
*/

//4.4.4
/**
 * @param {string} str77
 * @remark debugger语句主要用于除错，作用是设置断点。
 */
function first77 (str77){
    for(var i = 0; i < 5; i++){
        console.log(i);
        if (i === 2) debugger;
    }
}
// console.log(first77())//Chrome 浏览器中，当代码运行到debugger语句时，就会暂停运行，自动打开脚本源码界面。
